/**
 * Copyright 2013 yangming.liu<liuyangming@gmail.com>.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, see <http://www.gnu.org/licenses/>.
 */
package org.bytesoft.openjta;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class TransactionStatus implements Serializable {
	private static final long serialVersionUID = 1L;

	public static final int STATUS_ACTIVE = 0x0;
	public static final int STATUS_MARKED_ROLLBACK = 0x1;

	public static final int STATUS_PREPARING = 0x2;
	public static final int STATUS_PREPARE_FAIL = 0x4;
	public static final int STATUS_PREPARED = 0x8;

	public static final int STATUS_COMMITTING = 0x10;// 16
	public static final int STATUS_COMMIT_FAIL = 0x20;// 32
	public static final int STATUS_COMMITTED = 0x40;// 64

	public static final int STATUS_ROLLING_BACK = 0x80;// 128
	public static final int STATUS_ROLLBACK_FAIL = 0x100;// 256
	public static final int STATUS_ROLLEDBACK = 0x200;// 512

	public static final int STATUS_UNKNOWN = 0x400;// 1024

	private int currentStatus = TransactionStatus.STATUS_ACTIVE;
	private int lastestStatus = TransactionStatus.STATUS_ACTIVE;
	private int statusDetails = TransactionStatus.STATUS_ACTIVE;

	public TransactionStatus() {
	}

	public boolean isRollbackOnly() {
		return this.currentStatus == TransactionStatus.STATUS_MARKED_ROLLBACK;
	}

	public boolean isActive() {
		return this.currentStatus == TransactionStatus.STATUS_ACTIVE;
	}

	public boolean isPreparing() {
		return this.currentStatus == TransactionStatus.STATUS_PREPARING;
	}

	public boolean isStatusPreparing(boolean containsFail) {
		boolean preparing = this.isPreparing();
		if (containsFail) {
			boolean failure = this.isPrepareFail();
			return preparing || failure;
		} else {
			return preparing;
		}
	}

	public boolean isPrepareFail() {
		return this.currentStatus == TransactionStatus.STATUS_PREPARE_FAIL;
	}

	public boolean isPrepared() {
		return this.currentStatus == TransactionStatus.STATUS_PREPARED;
	}

	public boolean isCommitting() {
		return this.currentStatus == TransactionStatus.STATUS_COMMITTING;
	}

	public boolean isStatusCommitting(boolean containsFail) {
		boolean committing = this.isCommitting();
		if (containsFail) {
			boolean failure = this.isCommitFail();
			return committing || failure;
		} else {
			return committing;
		}
	}

	public boolean isCommitFail() {
		return this.currentStatus == TransactionStatus.STATUS_COMMIT_FAIL;
	}

	public boolean isCommitted() {
		return this.currentStatus == TransactionStatus.STATUS_COMMITTED;
	}

	public boolean isRollingBack() {
		return this.currentStatus == TransactionStatus.STATUS_ROLLING_BACK;
	}

	public boolean isStatusRollingBack(boolean containsFail) {
		boolean rolling = this.isRollingBack();
		if (containsFail) {
			boolean failure = this.isRollbackFail();
			return rolling || failure;
		} else {
			return rolling;
		}
	}

	public boolean isRollbackFail() {
		return this.currentStatus == TransactionStatus.STATUS_ROLLBACK_FAIL;
	}

	public boolean isRolledBack() {
		return this.currentStatus == TransactionStatus.STATUS_ROLLEDBACK;
	}

	public synchronized void markStatusRollback() throws IllegalStateException {
		if (this.lastestStatus == TransactionStatus.STATUS_COMMITTED
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.currentStatus == TransactionStatus.STATUS_COMMITTED
				|| this.currentStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.currentStatus == TransactionStatus.STATUS_ACTIVE
				|| this.currentStatus == TransactionStatus.STATUS_MARKED_ROLLBACK) {
			this.lastestStatus = TransactionStatus.STATUS_MARKED_ROLLBACK;
			this.statusDetails |= TransactionStatus.STATUS_MARKED_ROLLBACK;
		}
		this.currentStatus = TransactionStatus.STATUS_MARKED_ROLLBACK;
	}

	public synchronized void transferToPreparing() throws IllegalStateException {
		if (this.lastestStatus == TransactionStatus.STATUS_COMMITTED
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.currentStatus == TransactionStatus.STATUS_COMMITTED
				|| this.currentStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_ACTIVE //
				|| this.lastestStatus == TransactionStatus.STATUS_MARKED_ROLLBACK) {
			this.lastestStatus = TransactionStatus.STATUS_PREPARING;
			this.statusDetails |= TransactionStatus.STATUS_PREPARING;
		}

		this.currentStatus = TransactionStatus.STATUS_PREPARING;
	}

	public synchronized void transferToPrepareFail() throws IllegalStateException {
		if (this.lastestStatus == TransactionStatus.STATUS_COMMITTED
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.currentStatus == TransactionStatus.STATUS_COMMITTED
				|| this.currentStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_PREPARING) {
			this.lastestStatus = TransactionStatus.STATUS_PREPARE_FAIL;
			this.statusDetails |= TransactionStatus.STATUS_PREPARE_FAIL;
		}
		this.currentStatus = TransactionStatus.STATUS_PREPARE_FAIL;
	}

	public synchronized void transferToPrepared() throws IllegalStateException {
		if (this.lastestStatus == TransactionStatus.STATUS_COMMITTED
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.currentStatus == TransactionStatus.STATUS_COMMITTED
				|| this.currentStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_ACTIVE //
				|| this.lastestStatus == TransactionStatus.STATUS_MARKED_ROLLBACK) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_PREPARING//
				|| this.lastestStatus == TransactionStatus.STATUS_PREPARE_FAIL) {
			this.lastestStatus = TransactionStatus.STATUS_PREPARED;
			this.statusDetails |= TransactionStatus.STATUS_PREPARED;
		}
		this.currentStatus = TransactionStatus.STATUS_PREPARED;
	}

	public synchronized void transferToCommitting() throws IllegalStateException {
		if (this.lastestStatus == TransactionStatus.STATUS_COMMITTED
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.currentStatus == TransactionStatus.STATUS_COMMITTED
				|| this.currentStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_ACTIVE
				|| this.lastestStatus == TransactionStatus.STATUS_MARKED_ROLLBACK
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLING_BACK
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLBACK_FAIL) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_PREPARING //
				|| this.lastestStatus == TransactionStatus.STATUS_PREPARE_FAIL
				|| this.lastestStatus == TransactionStatus.STATUS_PREPARED) {
			this.lastestStatus = TransactionStatus.STATUS_COMMITTING;
			this.statusDetails |= TransactionStatus.STATUS_COMMITTING;
		}

		this.currentStatus = TransactionStatus.STATUS_COMMITTING;
	}

	public synchronized void transferToCommitFail() throws IllegalStateException {
		if (this.lastestStatus == TransactionStatus.STATUS_COMMITTED
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.currentStatus == TransactionStatus.STATUS_COMMITTED
				|| this.currentStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_ACTIVE
				|| this.lastestStatus == TransactionStatus.STATUS_MARKED_ROLLBACK
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLING_BACK
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLBACK_FAIL) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_COMMITTING) {
			this.lastestStatus = TransactionStatus.STATUS_COMMIT_FAIL;
			this.statusDetails |= TransactionStatus.STATUS_COMMIT_FAIL;
		}
		this.currentStatus = TransactionStatus.STATUS_COMMIT_FAIL;
	}

	public synchronized void transferToCommitted() throws IllegalStateException {
		if (this.lastestStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.currentStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_ACTIVE
				|| this.lastestStatus == TransactionStatus.STATUS_MARKED_ROLLBACK
				|| this.lastestStatus == TransactionStatus.STATUS_PREPARING
				|| this.lastestStatus == TransactionStatus.STATUS_PREPARE_FAIL
				|| this.lastestStatus == TransactionStatus.STATUS_PREPARED
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLING_BACK
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLBACK_FAIL) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_COMMITTING//
				|| this.lastestStatus == TransactionStatus.STATUS_COMMIT_FAIL) {
			this.lastestStatus = TransactionStatus.STATUS_COMMITTED;
			this.statusDetails |= TransactionStatus.STATUS_COMMITTED;
		}

		this.currentStatus = TransactionStatus.STATUS_COMMITTED;
	}

	public synchronized void transferToRollingback() throws IllegalStateException {
		if (this.lastestStatus == TransactionStatus.STATUS_COMMITTED
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_ROLLBACK_FAIL) {
			this.currentStatus = TransactionStatus.STATUS_ROLLING_BACK;
		} else {
			this.currentStatus = TransactionStatus.STATUS_ROLLING_BACK;
			this.lastestStatus = TransactionStatus.STATUS_ROLLING_BACK;
			this.statusDetails |= TransactionStatus.STATUS_ROLLING_BACK;
		}
	}

	public synchronized void transferToRollbackFail() throws IllegalStateException {
		if (this.lastestStatus == TransactionStatus.STATUS_COMMITTED
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.currentStatus == TransactionStatus.STATUS_COMMITTED
				|| this.currentStatus == TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_ROLLING_BACK) {
			this.lastestStatus = TransactionStatus.STATUS_ROLLBACK_FAIL;
			this.statusDetails |= TransactionStatus.STATUS_ROLLBACK_FAIL;
		}
		this.currentStatus = TransactionStatus.STATUS_ROLLBACK_FAIL;
	}

	public synchronized void transferToRolledback() throws IllegalStateException {
		if (this.lastestStatus == TransactionStatus.STATUS_COMMITTED) {
			throw new IllegalStateException();
		} else if (this.currentStatus == TransactionStatus.STATUS_COMMITTED) {
			throw new IllegalStateException();
		} else if (this.lastestStatus != TransactionStatus.STATUS_ROLLING_BACK//
				&& this.lastestStatus != TransactionStatus.STATUS_ROLLBACK_FAIL//
				&& this.lastestStatus != TransactionStatus.STATUS_ROLLEDBACK) {
			throw new IllegalStateException();
		} else if (this.lastestStatus == TransactionStatus.STATUS_ROLLING_BACK//
				|| this.lastestStatus == TransactionStatus.STATUS_ROLLBACK_FAIL) {
			this.lastestStatus = TransactionStatus.STATUS_ROLLEDBACK;
			this.statusDetails |= TransactionStatus.STATUS_ROLLEDBACK;
		}
		this.currentStatus = TransactionStatus.STATUS_ROLLEDBACK;
	}

	public String getStatusCode() {
		return this.getStatusCode(this.lastestStatus);
	}

	public String getStatusCode(int status) {
		Field[] fields = TransactionStatus.class.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			Field field = fields[i];

			int modifiers = field.getModifiers();
			boolean isPublic = Modifier.isPublic(modifiers);
			boolean isFinal = Modifier.isFinal(modifiers);
			boolean isStatic = Modifier.isStatic(modifiers);
			String fieldName = field.getName();
			if (isPublic && isFinal && isStatic && fieldName.startsWith("STATUS")) {
				try {
					Integer value = (Integer) field.get(TransactionStatus.class);
					if (value != null && status == value.intValue()) {
						return fieldName;
					}
				} catch (IllegalArgumentException ignore) {
				} catch (IllegalAccessException ignore) {
				} catch (RuntimeException ignore) {
				}
			} else {
				continue;
			}
		}
		return "STATUS_UNKNOWN";
	}

	public int getCurrentStatus() {
		return currentStatus;
	}

	public void setCurrentStatus(int currentStatus) {
		this.currentStatus = currentStatus;
	}

	public int getLastestStatus() {
		return lastestStatus;
	}

	public void setLastestStatus(int lastestStatus) {
		this.lastestStatus = lastestStatus;
	}

	public int getStatusDetails() {
		return statusDetails;
	}

	public void setStatusDetails(int statusDetails) {
		this.statusDetails = statusDetails;
	}

}
